package com.heima.wemedia.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.heima.common.aliyun.GreenImageUploadScan;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.common.delayTask.RedisDelayedQueueUtil;
import com.heima.common.enums.AppHttpCodeEnum;
import com.heima.common.exception.LeadException;
import com.heima.common.tess4j.Tess4jClient;
import com.heima.common.util.BeanHelper;
import com.heima.common.util.JsonUtils;
import com.heima.common.util.SensitiveWordUtil;
import com.heima.feign.client.ArticleFeign;
import com.heima.file.service.MinioTemplate;
import com.heima.model.media.dtos.WmNewsResultDTO;
import com.heima.wemedia.entity.WmNews;
import com.heima.wemedia.entity.WmSensitive;
import com.heima.wemedia.service.WmNewsAuditService;
import com.heima.wemedia.service.WmNewsService;
import com.heima.wemedia.service.WmSensitiveService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class WmNewsAuditServiceImpl implements WmNewsAuditService {

    @Autowired
    private ArticleFeign articleFeign;

    @Autowired
    private RedisDelayedQueueUtil delayedQueueUtil;
    /**
     * 文章审核
     * @param wmNews
     */
    @Async("taskExecutor")  //异步执行当前方法
    @Override
    @GlobalTransactional(rollbackFor = Exception.class) //使用分布式事务
    public void auditWmNews(WmNews wmNews) {
        log.info("=====auditWmNews，开始执行");
        if(wmNews==null){
            log.error("文章数据不存在！");
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }
        if(wmNews.getStatus() == null || wmNews.getStatus() !=1){
            log.error("文章状态异常！");
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }
//        获取文章id
        Integer wmNewsId = wmNews.getId();
//        从文章中获取文本和图片
        Map<String,Object> map = getTextAndImage(wmNews);
        String text = map.get("text").toString();
        List<byte[]> imageByteList = (List<byte[]>)map.get("image");
//        检测敏感词,如果检测不通过，修改状态，改为2
        boolean b2 = checkSensitive(text,wmNewsId);
        if(!b2){
//            敏感词检测不通过，结束
            log.error("敏感词检测不通过");
            return;
        }
        boolean b3 = handleImageSensitiveScan(imageByteList, wmNewsId);
        if(!b3){
//            敏感词检测不通过，结束
            log.error("图片敏感词检测不通过");
            return;
        }
//        调用阿里云检测文本，如果检测不通过，修改状态，改为2 / 3
        boolean b = checkText(text,wmNewsId);
        if(!b){
//            文本检测不通过，结束
            log.error("文本检测不通过");
            return;
        }
//        调用阿里云检测图片,如果检测不通过，修改状态，改为2 / 3
        boolean b1 = checkImage(imageByteList, wmNewsId);
        if(!b1){
//            图片检测不通过，结束
            log.error("图片检测不通过");
            return;
        }

//        修改文章状态，改为8 - 自动审核通过
        updateWmNewsStatus(wmNewsId,8,null,null);
//        判断发布时间，如果有发布时间，如果发布时间大于当前时间，就不发布了
        if(wmNews.getPublishTime() != null &&
           wmNews.getPublishTime().getTime() > System.currentTimeMillis()){
            log.info("发布时间大于当前时间，结束");
//            计算延迟时间
            long delaytime = wmNews.getPublishTime().getTime() - System.currentTimeMillis();
//            放入延迟队列
            log.info("把wmNewsId={},放入延迟队列",wmNewsId);
            delayedQueueUtil.addToQueue(wmNewsId,"wm.news.pub",delaytime, TimeUnit.MILLISECONDS);
            return ;
        }
//        远程调用article服务，文章发布
        publishWmNews(wmNews);

    }

    @Override
    public void publishWmNews(WmNews wmNews) {
        Integer wmNewsId = wmNews.getId();
        if(StringUtils.isBlank(wmNews.getTitle())){
//            如果标题是空，就是延迟，查询文章内容
            wmNews = wmNewsService.getById(wmNewsId);
        }
        //        对象类型转换，属性拷贝
        WmNewsResultDTO wmNewsResultDTO = BeanHelper.copyProperties(wmNews, WmNewsResultDTO.class);
        Long articleId = articleFeign.saveArticle(wmNewsResultDTO);
//        修改文章状态，改为9-发布成功
        updateWmNewsStatus(wmNewsId,9,null,articleId);
    }


    @Autowired
    private Tess4jClient tess4jClient;
    /**
     * 从图片中抓取文字，进行敏感词检测
     * @return
     */
    private boolean handleImageSensitiveScan(List<byte[]> imageByteList,Integer wmNewsId){
        if(CollectionUtils.isEmpty(imageByteList)){
//            没有图片
            return true;
        }
        log.info("图片ocr开始执行");
        for (byte[] imageBytes : imageByteList) {
//            使用字节数组构造  字节输入流
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(imageBytes);
            try{
                BufferedImage bufferedImage = ImageIO.read(byteArrayInputStream);
//        从图片中获取文字
                String text = tess4jClient.doOCR(bufferedImage);
                log.info("ocr,text={}",text);
//        进行敏感词检测
                boolean b = this.checkSensitive(text, wmNewsId);
                if(!b){
                    log.error("图片中包含敏感词，结束");
                    return false;
                }
            }catch (Exception e){
                log.error("图片ocr 失败！");
                return false;
            }
        }
        return true;
    }

    @Autowired
    private WmSensitiveService wmSensitiveService;
    /**
     * 检测敏感词
     * @param text
     * @param wmNewsId
     * @return
     */
    private boolean checkSensitive(String text, Integer wmNewsId) {

        log.info("==开始检测敏感词");
//        查询敏感词列表,只需要敏感词的list,后期可以放在Redis中
        List<String> wordList = wmSensitiveService.list().stream().map(WmSensitive::getSensitives).collect(Collectors.toList());
//        把敏感词列表处理成DFA的结构
//        检测用户输入的内容
        Map<String, Integer> map = SensitiveWordUtil.matchWords(wordList, text);
//        如果有敏感词，修改状态2-不通过，需要设置原因
        if(!CollectionUtils.isEmpty(map)){
//            包含敏感词,把敏感词组成字符串，做不通过的原因
            String reason = map.keySet().stream().collect(Collectors.joining(","));
//            修改状态
            updateWmNewsStatus(wmNewsId,2,"包含"+reason,null);
            return false;
        }
        return true;
    }

    @Autowired
    private MinioTemplate minioTemplate;
    /**
     *  从文章中获取文本和图片
     * @param wmNews
     * @return
     */
    private Map<String, Object> getTextAndImage(WmNews wmNews) {
        Map<String, Object> map = new HashMap<>();
        StringBuilder stringBuilder = new StringBuilder();
        List<byte[]> imageByteList = new ArrayList<>();
//        获取文本内容，包括 标题，content，标签
        stringBuilder.append(wmNews.getTitle());
//        把标签内容放入文本
        if(StringUtils.isNotBlank(wmNews.getLabels())){
            stringBuilder.append(",").append(wmNews.getLabels());
        }
//        获取图片集合，content，封面
//        把content转java对象
        List<Map<String,Object>> list = JsonUtils.nativeRead(wmNews.getContent(), new TypeReference<List<Map<String, Object>>>() {
        });
        if(!CollectionUtils.isEmpty(list)){
            for (Map<String, Object> contentMap : list) {
                String type = contentMap.get("type").toString();
                String value = contentMap.get("value").toString();
                if("text".equals(type)){
//                    文本内容
                    if(StringUtils.isNotBlank(value)) {
                        stringBuilder.append(",").append(value);
                    }
                }else{
//                    图片内容,从minio下载
                    byte[] bytes = minioTemplate.downLoadFile(value);
                    if(bytes!=null){
                        imageByteList.add(bytes);
                    }
                }
            }
        }
        if(StringUtils.isNotBlank(wmNews.getImages())){
//            封面有值
            String[] split = wmNews.getImages().split(",");
            for (String url : split) {
                if(StringUtils.isNotBlank(url)){
//                    封面图片,从minio下载
                    byte[] bytes = minioTemplate.downLoadFile(url);
                    if(bytes!=null){
                        imageByteList.add(bytes);
                    }
                }
            }
        }

        map.put("text",stringBuilder.toString());
        map.put("image",imageByteList);
        return map;
    }

    @Lazy  //延迟加载
    @Autowired
    private WmNewsService wmNewsService;

    /**
     * 修改文章的审核状态
     * @param wmNewsId
     * @param status 2-审核不通过 ，3-待人工审核，8-审核通过 9- 发布成功
     * @param reason  审核出问题的原因
     */
    private void updateWmNewsStatus(Integer wmNewsId, int status, String reason,Long articleId) {
        WmNews wmNews = new WmNews();
        wmNews.setId(wmNewsId);
        wmNews.setStatus(status);
        wmNews.setReason(reason);
        wmNews.setArticleId(articleId);
        wmNewsService.updateById(wmNews);
    }

    @Autowired
    private GreenTextScan textScan;

    /**
     * 调用阿里云文本检测
     * 如果检测不通过，修改状态，改为2 / 3
     * @param text
     * @param wmNewsId
     * @return
     */
    private  boolean checkText(String text,Integer wmNewsId){
        if(StringUtils.isBlank(text)){
            log.error("文本内容为空，结束");
            return false;
        }
        try {
            Map<String, String> map = textScan.greenTextScan(text);
            String suggestion = map.get("suggestion");
            if("pass".equals(suggestion)){
//                检测通过
                log.info("文本检测通过");
                return true;
            }else if("review".equals(suggestion)){
                log.error("阿里云文本不确定，需要人工检测");
//                修改文章状态
                updateWmNewsStatus(wmNewsId,3,map.get("reason"),null);
                return false;
            }else{
                log.error("阿里云文本检测不通过");
//                修改文章状态
                updateWmNewsStatus(wmNewsId,2,map.get("reason"),null);
                return false;
            }
        }catch (Exception e){
            log.error("文本检测异常！");
            e.printStackTrace();
            return false;
        }
    }

    @Autowired
    private GreenImageUploadScan imageUploadScan;

    /**
     * 调用阿里云图片检测接口
     * @param imageByteList
     * @param wmNewsId
     * @return
     */
    private boolean checkImage(List<byte[]> imageByteList,Integer wmNewsId){
        if(CollectionUtils.isEmpty(imageByteList)){
//            图片为空
            log.info("图片参数为空");
            return true;
        }
        try {
            Map<String, String> map = imageUploadScan.imageScan(imageByteList);
            String suggestion = map.get("suggestion");
            if("pass".equals(suggestion)){
//                检测通过
                log.info("图片检测通过");
                return true;
            }else if("review".equals(suggestion)){
                log.error("阿里云图片不确定，需要人工检测");
//                修改文章状态
                updateWmNewsStatus(wmNewsId,3,map.get("label"),null);
                return false;
            }else{
                log.error("阿里云图片检测不通过");
//                修改文章状态
                updateWmNewsStatus(wmNewsId,2,map.get("label"),null);
                return false;
            }
        }catch (Exception e){
            log.error("图片检测异常！");
            e.printStackTrace();
            return false;
        }
    }

}
